package com.xl.utils.util;

import com.xl.utils.util.bo.CosineSimilarityBO;
import com.xl.utils.util.bo.SimilarityAndDifferentSetBO;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author xionglin
 * @Classname CosineSimilarityUtil
 * @Description 余弦相似度计算
 * @Date 2021/1/22 10:17
 */
public class CosineSimilarityUtil {


    private CosineSimilarityUtil() {
    }

    /**
     * @param currentUserData 比如当前用户购买的商品id和评分的set集合
     * @param randomUsersData 比如某个用户购买的商品id和评分的set集合
     * @return
     */
    public static Double getCosSimilarity(HashSet<CosineSimilarityBO> currentUserData, HashSet<CosineSimilarityBO> randomUsersData) {
        if (currentUserData.isEmpty()) {
            //说明当前用户未发生行为数据（比如说每购买过商品。。。。）无法知道用户的喜好
            return 0d;
        }
        if (randomUsersData.isEmpty()) {
            //被分析用户未发生行为数据（比如说每购买过商品。。。。）说明被分析用户和当前用户相似度未0
            return 0d;
        }
        double mulScoreSum = 0;
        for (CosineSimilarityBO cData : currentUserData) {
            int cGoodId = cData.getGoodId();
            for (CosineSimilarityBO rData : randomUsersData) {
                int RgoodId = rData.getGoodId();
                if (cGoodId == RgoodId) {
                    mulScoreSum += cData.getScore() * rData.getScore();
                }
            }
        }
        double cDataPowSum = 0;
        for (CosineSimilarityBO cData : currentUserData) {
            Double score = cData.getScore();
            if (score == 0) {
                break;
            } else {
                //对分数的平方值求和
                cDataPowSum += Math.pow(score, 2);
            }

        }

        double rDataPowSum = 0;
        for (CosineSimilarityBO rData : randomUsersData) {
            Double score = rData.getScore();
            if (score == 0) {
                break;
            } else {
                //对分数的平方值求和
                rDataPowSum += Math.pow(score, 2);
            }

        }
        //带入夹角余弦的公式  sqrt:开根号运算   pow:次方运算
        double cosSimilarity = mulScoreSum / ((Math.sqrt(cDataPowSum)) * (Math.sqrt(rDataPowSum)));
        return cosSimilarity;
    }

    /**
     * 求randomUsersData中存在而在currentUserData不存在的数据，相当于求rrandomUsersData对于currentUserData的差级，作为被推送的行为数据
     *
     * @param currentUserData 比如当前用户购买的商品id和对应评分的set集合
     * @param randomUsersData 比如某个用户购买的商品id和对应评分的set集合
     * @return
     */
    public static HashSet getDifferentSet(HashSet<CosineSimilarityBO> currentUserData, HashSet<CosineSimilarityBO> randomUsersData) {
        if (currentUserData.isEmpty()) {
            //返回空set
            return new HashSet();
        }
        if (randomUsersData.isEmpty()) {
            //返回空set
            return new HashSet();
        }
        List<Integer> currentUserData_ = currentUserData.stream().map(CosineSimilarityBO::getGoodId).collect(Collectors.toList());
        List<Integer> randomUsersData_ = randomUsersData.stream().map(CosineSimilarityBO::getGoodId).collect(Collectors.toList());
        //求randomUsersData对于currentUserData差集
        HashSet<Integer> result = new HashSet<>();
        result.addAll(randomUsersData_);
        result.removeAll(currentUserData_);
        return result;
    }

    /**
     * 返回两数据集的余弦相似度计算，且返回randomUsersData对于currentUserData的差集合
     * @param currentUserData
     * @param randomUsersData
     * @return
     */
    public static SimilarityAndDifferentSetBO getCosSimilarityAndDifferentSet(HashSet<CosineSimilarityBO> currentUserData, HashSet<CosineSimilarityBO> randomUsersData) {
        HashSet differentSet = getDifferentSet(currentUserData, randomUsersData);
        Double cosSimilarity = getCosSimilarity(currentUserData, randomUsersData);
        SimilarityAndDifferentSetBO similarityAndDifferentSetBO = new SimilarityAndDifferentSetBO();
        similarityAndDifferentSetBO.setDifferentSet(differentSet);
        similarityAndDifferentSetBO.setSimilarity(cosSimilarity);
        return similarityAndDifferentSetBO;
    }

    /**
     * 返回两数据集的余弦相似度计算，且返回randomUsersData对于currentUserData的差集合
     * @param currentUserData
     * @param randomUsersDataList
     * @return
     */
    public static List<SimilarityAndDifferentSetBO> getCosSimilarityAndDifferentSet(HashSet<CosineSimilarityBO> currentUserData, List<HashSet<CosineSimilarityBO>> randomUsersDataList) {
        if (currentUserData.isEmpty()) {
            //如果当前用户行为数据为空，返回空list
            return new ArrayList<>();
        }
        if (randomUsersDataList.isEmpty()) {
            //系统不存在行为数据无法分析，返回空list
            return new ArrayList<>();
        }
        //返回数据
        ArrayList<SimilarityAndDifferentSetBO> resultInfo = new ArrayList<>();
        //循环处理
        for (HashSet element : randomUsersDataList) {
            resultInfo.add(getCosSimilarityAndDifferentSet(currentUserData, element));
        }
        //按余弦相似度降序排序
        List<SimilarityAndDifferentSetBO> result = resultInfo.stream().sorted(Comparator.comparing(SimilarityAndDifferentSetBO::getSimilarity).reversed()).collect(Collectors.toList());
        return result;
    }

    /**
     * 测试
     *
     * @param args
     */
    public static void main(String[] args) {
        CosineSimilarityBO cs1 = new CosineSimilarityBO();
        cs1.setGoodId(1);
        cs1.setScore(1.5);

        CosineSimilarityBO cs2 = new CosineSimilarityBO();
        cs2.setGoodId(2);
        cs2.setScore(2.5);

        CosineSimilarityBO cs3 = new CosineSimilarityBO();
        cs3.setGoodId(3);
        cs3.setScore(0.5);
        HashSet<CosineSimilarityBO> cdata = new HashSet<>();
        cdata.add(cs1);
        cdata.add(cs3);
        HashSet<CosineSimilarityBO> rdata = new HashSet<>();
        rdata.add(cs1);
        cdata.add(cs2);
        rdata.add(cs3);


        System.out.println(getCosSimilarityAndDifferentSet(rdata, cdata));
    }

}
